#!/usr/bin/env bash

set -euo pipefail

MYSQL_PKG="${MYSQL_PKG:-}"
INSTALL_BASE="/opt/mysql"
MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-hmgsoft}"
SERVICE_NAME="mysql57"
MYSQL_USER="mysql"
MYSQL_GROUP="mysql"
TIMESTAMP="$(date +%Y%m%d%H%M%S)"
MYSQL_TAR_DIR=""
MYSQL_VERSION=""
MYSQL_VERSION_DIR_NAME=""
PACKAGE_MANAGER=""

log() {
  echo "[$(date '+%F %T')] $*"
}

abort() {
  echo "[$(date '+%F %T')] [ERROR] $*" >&2
  exit 1
}

require_root() {
  if [[ "$(id -u)" -ne 0 ]]; then
    abort "请使用 root 用户或 sudo 运行该脚本。"
  fi
}

assert_command() {
  local cmd="$1"
  local pkg_hint="$2"
  if ! command -v "${cmd}" >/dev/null 2>&1; then
    abort "缺少命令 ${cmd}，请先安装依赖：${pkg_hint}"
  fi
}

detect_package_manager() {
  if command -v yum >/dev/null 2>&1; then
    PACKAGE_MANAGER="yum"
  elif command -v dnf >/dev/null 2>&1; then
    PACKAGE_MANAGER="dnf"
  elif command -v apt-get >/dev/null 2>&1; then
    PACKAGE_MANAGER="apt-get"
  elif command -v zypper >/dev/null 2>&1; then
    PACKAGE_MANAGER="zypper"
  else
    PACKAGE_MANAGER=""
  fi
}

install_dependencies() {
  detect_package_manager

  local missing=()
  case "${PACKAGE_MANAGER}" in
    yum|dnf)
      local rpm_deps=(libaio numactl-libs xz tar)
      for pkg in "${rpm_deps[@]}"; do
        if ! rpm -q "${pkg}" >/dev/null 2>&1; then
          missing+=("${pkg}")
        fi
      done
      if (( ${#missing[@]} > 0 )); then
        log "检测到缺失依赖：${missing[*]}，尝试使用 ${PACKAGE_MANAGER} 安装"
        "${PACKAGE_MANAGER}" install -y "${missing[@]}"
      else
        log "依赖已满足，无需安装。"
      fi
      ;;
    apt-get)
      local apt_deps=(libaio1 libnuma1 xz-utils tar)
      for pkg in "${apt_deps[@]}"; do
        if ! dpkg -s "${pkg}" >/dev/null 2>&1; then
          missing+=("${pkg}")
        fi
      done
      if (( ${#missing[@]} > 0 )); then
        log "检测到缺失依赖：${missing[*]}，尝试使用 apt-get 安装"
        DEBIAN_FRONTEND=noninteractive apt-get install -y "${missing[@]}"
      else
        log "依赖已满足，无需安装。"
      fi
      ;;
    zypper)
      local zypper_deps=(libaio numactl xz tar)
      for pkg in "${zypper_deps[@]}"; do
        if ! rpm -q "${pkg}" >/dev/null 2>&1; then
          missing+=("${pkg}")
        fi
      done
      if (( ${#missing[@]} > 0 )); then
        log "检测到缺失依赖：${missing[*]}，尝试使用 zypper 安装"
        zypper --non-interactive install "${missing[@]}"
      else
        log "依赖已满足，无需安装。"
      fi
      ;;
    *)
      log "未检测到受支持的包管理器，若依赖缺失请手工安装：libaio/numactl/xz/tar。"
      ;;
  esac
}

detect_package() {
  local script_dir="$1"

  if [[ -n "${MYSQL_PKG}" ]]; then
    if [[ ! -f "${script_dir}/${MYSQL_PKG}" ]]; then
      abort "指定的 MYSQL_PKG=${MYSQL_PKG} 不存在，请确认路径。"
    fi
  else
    local candidates=()
    while IFS= read -r -d '' file; do
      candidates+=("$(basename "${file}")")
    done < <(find "${script_dir}" -maxdepth 1 -type f -name 'mysql-*-linux-*.tar.gz' -print0)

    if [[ ${#candidates[@]} -eq 0 ]]; then
      abort "未在 ${script_dir} 中找到 mysql-*-linux-*.tar.gz 安装包，请将安装包与脚本放在同一目录。"
    fi

    if [[ ${#candidates[@]} -gt 1 ]]; then
      abort "检测到多个安装包：${candidates[*]}，请通过环境变量 MYSQL_PKG 指定要安装的包。"
    fi
    MYSQL_PKG="${candidates[0]}"
  fi

  MYSQL_TAR_DIR="${MYSQL_PKG%.tar.gz}"
  if [[ -z "${MYSQL_TAR_DIR}" ]]; then
    abort "无法解析安装包名称 ${MYSQL_PKG}"
  fi
  MYSQL_VERSION="$(sed -n 's/^mysql-\([0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' <<<"${MYSQL_TAR_DIR}")"
  if [[ -n "${MYSQL_VERSION}" ]]; then
    MYSQL_VERSION_DIR_NAME="mysql-${MYSQL_VERSION}"
  else
    MYSQL_VERSION_DIR_NAME="${MYSQL_TAR_DIR}"
  fi
}

prepare_user_group() {
  if ! getent group "${MYSQL_GROUP}" >/dev/null 2>&1; then
    log "创建用户组 ${MYSQL_GROUP}"
    groupadd --system "${MYSQL_GROUP}"
  fi
  if ! id "${MYSQL_USER}" >/dev/null 2>&1; then
    log "创建系统用户 ${MYSQL_USER}"
    useradd --system --no-create-home --gid "${MYSQL_GROUP}" --shell /sbin/nologin "${MYSQL_USER}"
  fi
}

prepare_directories() {
  log "准备目录结构"
  mkdir -p "${INSTALL_BASE}"
  mkdir -p /var/lib/mysql
  mkdir -p /var/run/mysqld
  chown -R "${MYSQL_USER}:${MYSQL_GROUP}" /var/lib/mysql /var/run/mysqld
  chmod 750 /var/run/mysqld
}

extract_mysql() {
  local script_dir="$1"
  local pkg_path="${script_dir}/${MYSQL_PKG}"
  local extracted_dir="${INSTALL_BASE}/${MYSQL_TAR_DIR}"
  local version_dir="${INSTALL_BASE}/${MYSQL_VERSION_DIR_NAME}"

  if [[ ! -f "${pkg_path}" ]]; then
    abort "未在 ${script_dir} 中找到 ${MYSQL_PKG}，请将安装包与脚本置于同一目录。"
  fi

  if [[ -d "${version_dir}" ]]; then
    log "检测到已存在目录 ${version_dir}，跳过解压。"
  else
    log "解压安装包至 ${INSTALL_BASE}"
    tar -xzf "${pkg_path}" -C "${INSTALL_BASE}"
    if [[ "${extracted_dir}" != "${version_dir}" && -d "${extracted_dir}" ]]; then
      mv "${extracted_dir}" "${version_dir}"
    fi
  fi

  ln -sfn "${version_dir}" "${INSTALL_BASE}/mysql"
  chown -R "${MYSQL_USER}:${MYSQL_GROUP}" "${version_dir}"
}

backup_file() {
  local file="$1"
  if [[ -f "${file}" ]]; then
    cp -a "${file}" "${file}.bak.${TIMESTAMP}"
    log "已备份 ${file} 至 ${file}.bak.${TIMESTAMP}"
  fi
}

write_my_cnf() {
  local basedir="${INSTALL_BASE}/mysql"
  local datadir="${basedir}/data"
  local logdir="${basedir}/logs"
  local tmpdir="${basedir}/tmp"

  mkdir -p "${datadir}" "${logdir}" "${tmpdir}"
  chown -R "${MYSQL_USER}:${MYSQL_GROUP}" "${datadir}" "${logdir}" "${tmpdir}"

  backup_file "/etc/my.cnf"
  cat > /etc/my.cnf <<EOF
[client]
socket=/var/lib/mysql/mysql.sock
default-character-set=utf8mb4

[mysql]
socket=/var/lib/mysql/mysql.sock
default-character-set=utf8mb4

[mysqld]
user=${MYSQL_USER}
basedir=${basedir}
datadir=${datadir}
tmpdir=${tmpdir}
socket=/var/lib/mysql/mysql.sock
pid-file=/var/run/mysqld/mysqld.pid
log-error=${logdir}/mysqld-error.log

bind-address=0.0.0.0
port=3306

character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
explicit_defaults_for_timestamp=ON
lower_case_table_names=1
max_allowed_packet=134217728
max_connections=10000
sql_mode=STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO
group_concat_max_len=102400
default-time_zone='+8:00'

innodb_buffer_pool_size=512M
innodb_log_file_size=256M
innodb_flush_method=O_DIRECT
innodb_file_per_table=ON
EOF

  chown root:root /etc/my.cnf
  chmod 644 /etc/my.cnf
}

initialize_database() {
  local basedir="${INSTALL_BASE}/mysql"
  local datadir="${basedir}/data"

  if [[ -d "${datadir}" && -n "$(ls -A "${datadir}" 2>/dev/null)" ]]; then
    log "检测到数据目录已存在内容，跳过初始化。"
    return
  fi

  log "初始化数据目录"
  rm -rf "${datadir}"
  mkdir -p "${datadir}"
  chown -R "${MYSQL_USER}:${MYSQL_GROUP}" "${datadir}"

  "${basedir}/bin/mysqld" --defaults-file=/etc/my.cnf --initialize-insecure --user="${MYSQL_USER}"
  chown -R "${MYSQL_USER}:${MYSQL_GROUP}" "${datadir}"
}

write_systemd_service() {
  local service_file="/etc/systemd/system/${SERVICE_NAME}.service"
  local basedir="${INSTALL_BASE}/mysql"

  backup_file "${service_file}"
  cat > "${service_file}" <<EOF
[Unit]
Description=MySQL 5.7 Server
After=network.target

[Service]
Type=forking
User=${MYSQL_USER}
Group=${MYSQL_GROUP}
PIDFile=/var/run/mysqld/mysqld.pid
LimitNOFILE=65535
Environment=TZ=Asia/Shanghai
ExecStart=${basedir}/bin/mysqld --defaults-file=/etc/my.cnf --daemonize
Restart=on-failure
TimeoutSec=300
KillSignal=SIGTERM

[Install]
WantedBy=multi-user.target
EOF

  chmod 644 "${service_file}"
  systemctl daemon-reload
}

start_service() {
  log "启动并设置 ${SERVICE_NAME} 服务"
  systemctl enable "${SERVICE_NAME}" >/dev/null 2>&1 || true
  systemctl restart "${SERVICE_NAME}"
}

wait_for_socket() {
  local socket="/var/lib/mysql/mysql.sock"
  for _ in {1..30}; do
    if [[ -S "${socket}" ]]; then
      return 0
    fi
    sleep 2
  done
  abort "等待 MySQL 启动超时，未检测到 ${socket}"
}

set_root_password() {
  local socket="/var/lib/mysql/mysql.sock"
  local mysql_bin="${INSTALL_BASE}/mysql/bin/mysql"

  if "${mysql_bin}" -uroot -p"${MYSQL_ROOT_PASSWORD}" -S "${socket}" -e "SELECT 1;" >/dev/null 2>&1; then
    log "root 密码已配置，跳过设置。"
    return
  fi

  if "${mysql_bin}" -uroot -S "${socket}" -e "SELECT 1;" >/dev/null 2>&1; then
    log "设置 root 密码"
    "${mysql_bin}" -uroot -S "${socket}" <<SQL
ALTER USER 'root'@'localhost' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}';
FLUSH PRIVILEGES;
SQL
  else
    abort "无法以空密码登录 root，请手动检查服务状态或已有密码。"
  fi
}

create_remote_root_user() {
  local socket="/var/lib/mysql/mysql.sock"
  local mysql_bin="${INSTALL_BASE}/mysql/bin/mysql"

  log "配置 root 用户允许所有 IP 访问"
  "${mysql_bin}" -uroot -p"${MYSQL_ROOT_PASSWORD}" -S "${socket}" <<SQL
-- 检查并创建 root@'%' 用户（如果不存在）
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '${MYSQL_ROOT_PASSWORD}' WITH GRANT OPTION;
-- 刷新权限
FLUSH PRIVILEGES;
SQL

  if [[ $? -eq 0 ]]; then
    log "root@'%' 用户配置成功，所有 IP 均可访问。"
  else
    log "警告: 配置 root@'%' 用户时可能遇到问题，请手动检查。"
  fi

  log "删除 root@'localhost' 账户以避免远程登录冲突"
  "${mysql_bin}" -uroot -p"${MYSQL_ROOT_PASSWORD}" -S "${socket}" <<SQL
-- 删除 root@'localhost' 账户
DROP USER IF EXISTS 'root'@'localhost';
-- 刷新权限
FLUSH PRIVILEGES;
SQL

  if [[ $? -eq 0 ]]; then
    log "root@'localhost' 账户已删除，仅保留 root@'%' 用于远程访问。"
  else
    log "警告: 删除 root@'localhost' 账户时可能遇到问题，请手动检查。"
  fi
}

configure_firewalld_port() {
  local port="3306"
  if ! command -v firewall-cmd >/dev/null 2>&1; then
    log "未检测到 firewall-cmd，跳过 firewalld 端口放行。"
    return
  fi
  if systemctl is-active --quiet firewalld; then
    if firewall-cmd --list-ports | grep -qw "${port}/tcp"; then
      log "firewalld 已放行端口 ${port}/tcp，跳过。"
    else
      log "配置 firewalld 放行端口 ${port}/tcp"
      firewall-cmd --permanent --add-port=${port}/tcp
      firewall-cmd --reload
    fi
  else
    log "firewalld 未运行，跳过端口放行。"
  fi
}

main() {
  require_root
  local script_dir
  script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

  detect_package "${script_dir}"
  install_dependencies
  assert_command tar "tar (通常系统已自带)"
  assert_command systemctl "systemd (确保目标系统使用 systemd)"
  prepare_user_group
  prepare_directories
  extract_mysql "${script_dir}"
  write_my_cnf
  initialize_database
  write_systemd_service
  start_service
  wait_for_socket
  set_root_password
  create_remote_root_user
  configure_firewalld_port

  log "MySQL 5.7 安装完成。"
  log "使用方式示例：${INSTALL_BASE}/mysql/bin/mysql -uroot -p${MYSQL_ROOT_PASSWORD} -S /var/lib/mysql/mysql.sock"
  log "远程连接示例：mysql -uroot -p${MYSQL_ROOT_PASSWORD} -h <服务器IP> -P 3306"
}

main "$@"

